package boerse.main.Account;

import boerse.main.Asset.CashAccount;

import boerse.main.Asset.Share;
import boerse.main.Asset.ShareDeposit;
import boerse.main.Comparator.*;
import boerse.main.Exceptions.NegativeShareCountException;
import boerse.main.Exceptions.NegativeValueException;
import boerse.main.Exceptions.NoSuchPlayerException;
import boerse.main.Exceptions.NotEnoughMoneyException;
import boerse.main.StockPriceProvider.StockPriceInfo;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.util.*;
/**
 * Handle all Accounts and do Actions on them
 * @author Domi
 *
 */
public class AccountManagerImpl implements AccountManager {

		private Hashtable<String, Player> allPlayer;
		private StockPriceInfo priceInfo;
		

		
        public AccountManagerImpl(StockPriceInfo priceInfo) {
            this.priceInfo = priceInfo;
            allPlayer = new Hashtable<String, Player>();
        }

		@Override
		public void createPlayer(String name)  {
			CashAccount cashAccount = new CashAccount(name + "`s Account");
			ShareDeposit shareDeposit = new ShareDeposit(name + "`s Share Depo");
			
			allPlayer.put(name, new Player(name, cashAccount, shareDeposit));
		}

		@Override
		public void buyShare(String playerName, String shareName, int count) throws NotEnoughMoneyException, NegativeValueException {
			Player player = searchPlayer(playerName);	
			Share searchedShare = priceInfo.getShare(shareName);
			
			player.getShareDeposit().buy(searchedShare, count, player.getCashAccount());
			
			
			player.addTransfair(new ActionLog(getDate(), count, searchedShare));
		}




		@Override
		public void sellShare(String playerName, String shareName, int count) throws NegativeValueException, NegativeShareCountException {
			Player player = searchPlayer(playerName);	
			Share searchedShare = priceInfo.getShare(shareName);
			
			player.getShareDeposit().sell(searchedShare, count, player.getCashAccount());
			
			player.addTransfair(new ActionLog(getDate(), count - 2*count, searchedShare));
				
				
		}

		@Override
		public long getCashAccountPrice(String playerName) {					
			return searchPlayer(playerName).getCashAccount().determinePrice();
		}
		
		@Override
		public long getShareDepositPrice(String playerName) {
			return searchPlayer(playerName).getShareDeposit().determinePrice();
		}

		@Override
		public long getAllAssets(String playername) {
			
			Player player = searchPlayer(playername);
			long price = 0;
				
			price = player.getCashAccount().determinePrice() + player.getShareDeposit().determinePrice();
				
			return price;
		
		}
		
		
		@Override
		public void addMoney(String playername, int money) throws NegativeValueException {
				searchPlayer(playername).getCashAccount().addMoney(money);			
		}
		
		
		@Override
        public void useAgent(String name) {
            Player searchedPlayer = searchPlayer(name);
            Agent agent = new Agent(priceInfo, searchedPlayer);
            agent.startTrade();
        }
        
		@Override
		public	String getShareDeposit(String name) {
			Player player = searchPlayer(name);
        	return player.getShareDeposit().toString();
		}
		
		@Override
		public String getAllShare() {
			Share[] allShares = priceInfo.getAllShares();
			String s = "";
			
			for (int i= 0; i < allShares.length; i++) {
				s += allShares[i].toString() + "\n"; 
			}
			
			return s;
		}
		
		@Override
		public String getAllPlayer() {
			Collection<Player> values = allPlayer.values();
			String allPlayerString = "";
			
			for (Player p : values) {
				allPlayerString += p.getName() + "\n";
			}
			return allPlayerString;
		}
		
		
//		public String getPlayer(String name) {
//			return searchPlayer(name).getName();
//		}
		
		/**
		 * Search player in player List by playername
		 * @param name
		 * @return searched Player
		 */
		private Player searchPlayer(String name) {
			Player player = allPlayer.get(name);
			
			if (player == null)
				throw new NoSuchPlayerException("Player " + name + " not found");
			
			return player;
		}
		
		/**
		 * Get actual Date instance
		 * @return String of actual Date
		 */
		private String getDate() {
		        Calendar cal = Calendar.getInstance();
		        Date date = cal.getTime();
		        DateFormat dateFormatter = DateFormat.getDateTimeInstance();
		        String newDate = dateFormatter.format(date);
		        newDate = newDate.replace(" ", "__").replace( ".", "_").replace(":","_");
		        return newDate;
		    }

		@Override
		public String getTransfers(String playerName, String outputModeString) {
			return workOnList(searchPlayer(playerName).getTransfair(), outputModeString);
		}

		
		@Override
		public String getTransfers(String playerName, String sortModeString, String outputModeString) {
			
			Comparator<ActionLog> comparator = getComparator(sortModeString);
			ArrayList<ActionLog> list = searchPlayer(playerName).getTransfair();
			
			Collections.sort(list, comparator);
			
			return workOnList(list, outputModeString);
		}


		@Override
		public String getTransfers(String playerName, String sortModeString, String shareName, String outputModeString) {
			ArrayList<ActionLog> list = searchPlayer(playerName).getTransfair();

			ArrayList<ActionLog> searchedList = new ArrayList<ActionLog>();
			
			for (ActionLog a : list) {
				if (a.getShare().getName().equals(shareName))
					searchedList.add(a);
			}
				
			list = searchedList;
			
			Comparator<ActionLog> comparator = getComparator(sortModeString);
			Collections.sort(list, comparator);
				
			return workOnList(list, outputModeString);
		}
			
		/**
		 * Get the Comparator for the sortMode
		 * @param sortModeString
		 * @return Comparator for sorting
		 */
		private Comparator<ActionLog> getComparator(String sortModeString) {
			try {
				SortMode sortMode = SortMode.valueOf(sortModeString.toUpperCase());
				Comparator<ActionLog> comparator;
					
				switch(sortMode) {
					case COUNT:
						comparator = new ActionLogCountComparator();
						break;
					
					case DATE:
						comparator = new ActionLogTimeComparator();
						break;
						
					case SHARE:
						comparator = new ActionLogShareComparator();
						break;
						
					default:
						comparator = new ActionLogTimeComparator();
						break;
				
				}
					
				return comparator;
				
			} catch(IllegalArgumentException e) {
				throw new IllegalArgumentException("No Such Line");
			}
		}
		
		/**
		 * Get the given list as layouted String
		 * @param list
		 * @return String from the list
		 */
		private String getListAsString(ArrayList<ActionLog> list) {
			String transfairs = "";
	
			for (ActionLog a : list) {
				transfairs +=  a.getDate() + " " + a.getCount() + " " + a.getShare().getName() + "\n";
			}
			return transfairs;
		}
		
		/**
		 * Put the list on the right output. When output is the Console then you get the list as a String
		 * @param list
		 * @param outputModeString
		 * @return String from the list or a String "";
		 */
		private String workOnList(ArrayList<ActionLog> list, String outputModeString) {
			try {
				OutputMode outputMode = OutputMode.valueOf(outputModeString.toUpperCase());
				
				switch (outputMode) {
				case CONSOLE:		
					return getListAsString(list);
				
				case HTML:
					writeListInFile(list);
					return "";
					
				default:
					return null;
				}
						
			} catch (IllegalArgumentException e) {
				throw new IllegalArgumentException("No such output");
			} 
		}
		
		/**
		 * Write the list in a fixed File
		 * @param list
		 */
		private void writeListInFile(ArrayList<ActionLog> list) {
			try {
				FileWriter out = new FileWriter("transfers.html");
				
				for (ActionLog a : list) {
					out.write("<BR>" + a.getDate() + ": " + a.getCount() + " " + a.getShare().getName() + "<BR>");
				}
				
				out.flush();
				out.close();
				
			}  catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		@Override
		public Timer getTimer() {
			return priceInfo.getTime();
		}
}
